<?php
header("Content-Type: application/octet-stream");
//header("Content-Type: text/plain");

$MAX_OBJECTS = 100000;
$EXPRESSION_SEP_CHAR = '<->';
$ATTRIBUTES_SEP_CHAR = ',';

// error codes
$DB_CONNECTION_ERROR = -1;
$QUERY_ERROR = -2;
$QUERY_PROCESS_ERROR = -3;
$DB_DOES_NOT_SET = -11;
$TABLE_DOES_NOT_SET = -12;
$COLUMN_DOES_NOT_SET = -13;
$EXTENT_DOES_NOT_SET = -14;

// constants
$ERROR_LOG_HEADER = "[AzukiMap " . __FILE__ . "]";
$NO_EXPRESSION_ID = -1;

// DB settings 
$PG_USER = "postgres";
$PG_PASS = "postgres";
$PG_HOST = "localhost";
$PG_PORT = 5432;
$GID_COLUMN = "gid";

$DEBUG_SQL = true;

$db = isset($_REQUEST['db']) ? $_REQUEST['db'] : null;
$table = isset($_REQUEST['table']) ? $_REQUEST['table'] : null;
$column = isset($_REQUEST['column']) ? $_REQUEST['column'] : null ;
$currentExtent = isset($_REQUEST['cr']) ? $_REQUEST['cr'] : null;
$previousExtent = isset($_REQUEST['pr']) ? $_REQUEST['pr'] : null;
$tolerance = isset($_REQUEST['tolerance']) ? $_REQUEST['tolerance'] : 1;
$filter = isset($_REQUEST['f']) ? $_REQUEST['f'] : null;
$srid = isset($_REQUEST['srid']) ? $_REQUEST['srid'] : 4326;
$expressions = isset($_REQUEST['exs']) ? split($EXPRESSION_SEP_CHAR, $_REQUEST['exs']) : Array();
$attributes = isset($_REQUEST['ats']) ? split($ATTRIBUTES_SEP_CHAR, $_REQUEST['ats']) : Array();

// buffer every outputs to get content size.
ob_start();

$hasParamError = checkInputParams($db, $table, $column, $currentExtent);
$sql = createSql($table, $column, $filter, $currentExtent, $previousExtent, $expressions, $attributes, $tolerance, $srid);
if ( PHP_OS != "WIN32" && PHP_OS != "WINNT") {
//	$sql = str_replace('\', '', $sql); 
}
query($db, $sql);

// set content length 
$cLength = ob_get_length();
header("Content-Length: " . $cLength);

// send data 
ob_end_flush();

exit;

function checkInputParams($db, $table, $column, $currentExtent) {
	global $DB_DOES_NOT_SET, $TABLE_DOES_NOT_SET, $COLUMN_DOES_NOT_SET, $EXTENT_DOES_NOT_SET;
	$hasParamError = false;
	if ( $db == null ) {
		printPackedInt($DB_DOES_NOT_SET);
		errorOut("line:" . __LINE__ . " DB parm does not set.");
		$hasParamError = true;
	}
	
	if ( $table == null ) {
		printPackedInt($TABLE_DOES_NOT_SET);
		errorOut("line:" . __LINE__ . " Table parm does not set.");
		$hasParamError = true;
	}
 	
	if ( $column == null ) {
		printPackedInt($COLUMN_DOES_NOT_SET);
		errorOut("line:" . __LINE__ . " Column parm does not set.");
		$hasParamError = true;
	}
	
	if ( $currentExtent == null ) {
		printPackedInt($EXTENT_DOES_NOT_SET);
		errorOut("line:" . __LINE__ . " Map Extent parm does not set.");
		$hasParamError = true;
	}
	return $hasParamError;
}

function createSql($table, $column, $filter, $currentExtent, $previousExtent, $expressions, $attributes, $tolerance, $srid) {
	global $NO_EXPRESSION_ID, $MAX_OBJECTS, $DEBUG_SQL, $GID_COLUMN;
	$sql = "select st_asbinary(st_transform(st_simplify($column, $tolerance),$srid), 'NDR') as geom, ";
	$sql .= createExpressionSql($expressions);
	$sql .= "$GID_COLUMN ";
	$sql .= createAttributesSql($attributes);
	// limit by bouding box.
	//$sql .= "from \"$table\" where ";
	//  for old version of PostGIS (<1.3.1?);
	$sql .= "from \"$table\" where $column && st_geomfromtext('$currentExtent', $srid ) ";
	// set filter
	if ( is_null($filter) ) {
		$sql .= "and " . $filter . " ";
	}
	if (! is_null($filter) && $filter != "" ) {
		$sql .= "and " . $filter . " ";
	}

	// limit by current extent polygon.
	$sql .= "and st_intersects(st_transform(st_geomfromtext('$currentExtent', $srid), srid($column)), $column) ";
	// except objects within previous extent.
	if ( $previousExtent != "" ) {
	  $sql .= "and not st_intersects(st_transform(st_geomfromtext('$previousExtent', $srid), srid($column)), $column) ";
    }
	// set max objects.
	$sql .= "limit $MAX_OBJECTS";

	if ( $DEBUG_SQL ) {
		errorOut("sql debug: $sql");
	}

	return $sql;
}

function createExpressionSql($expressions) {
	global $NO_EXPRESSION_ID;
	$sql = "";
	// set phase to set expressions.
	if ( count($expressions) == 0 || $expressions[0] == "" ) {
		$sql .= "$NO_EXPRESSION_ID ,";
	} else {
		$sql .= "case ";
		$i = 0;
		foreach ( $expressions as $expr ) {
			if ( $expr != "" ) {
				$sql .= "when ($expr) then $i ";
				$i++;
			}
		}
		$sql .= "else $NO_EXPRESSION_ID end, ";
	}
	return $sql;
}

function createAttributesSql($attributes) {
	if ( count($attributes) == 0 || $attributes[0] == "" ) {
		return "";
	}
	$sql = "";
	for( $i = 0; $i < count($attributes); $i++ ) {
		$sql .= ",";
		$sql .= $attributes[$i] . " ";
	}
	return $sql;
}

// Query and make output.
function query($db, $sql) {
	global $PG_HOST, $PG_USER, $PG_PASS;
	global $QUERY_ERROR, $QUERY_PROCESS_ERROR, $DB_CONNECTION_ERROR;
	try {
		$pdo = new PDO("pgsql:host=$PG_HOST; dbname=$db", $PG_USER, $PG_PASS);
		try {
			$stmt = $pdo->query($sql);
			// check stmt
			// if a sql has a problem, return "-2" as header
			if ( $stmt ) {
				$count = $stmt->rowCount();
				printPackedInt($count);
				if ( $count > 0 ) {
					while($row = $stmt->fetch(PDO::FETCH_NUM)){
						printPackedInt($row[1]);	// expression id
						printPackedInt($row[2]);	// gid
						printPackedInt(count($row) - 3);	// number of attributes
						printAttributes($row);		// attributes by string
						fpassthru($row[0]);			// geometry
					}
				}
			} else { 
				printPackedInt($QUERY_ERROR);
				errorOut("line:" . __LINE__ . " Query has a problem. SQL=$sql");
			}
		} catch(PDOException $e){
			printPackedInt($QUERY_PROCESS_ERROR);
			errorOut("line:" . __LINE__ . " Query process has a problem. ". $e->getMessage());
		}
		$pdo = null;
	} catch(PDOException $e){
		// if the connection problem is occured, return "-1" as header
		printPackedInt($DB_CONNECTION_ERROR);
		errorOut("line:" . __LINE__ . " DB connection has a problem. ". $e->getMessage());
	}
}

function printAttributes($row) {
	for ( $i = 3; $i < count($row); $i++) {
		// doesn't work
		//print7BitEncodedInt(strlen($row[$i]));
		printPackedInt(mb_strlen($row[$i], "UTF-8"));
		print($row[$i]);
	}
}

function printPackedInt($str) {
	$packedCount = pack("i", $str);
	print($packedCount);
}

// doesn't work...
function print7BitEncodedInt($value)
{
    $num = $value;
    while ($num >= 0x80)
    {
        print($num | 0x80);
        $num = $num >> 7;
    }
    print($num);
}


function errorOut($message) {
	global $ERROR_LOG_HEADER;
	error_log("$ERROR_LOG_HEADER $message");
}
?>
